-
Notifications
You must be signed in to change notification settings - Fork 981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Fish support for environments #15503
base: develop2
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, just I agree it would need a bit more testing of functionality and possible interactions with other shells.
Well, I'm officially converted to the cult of Fish now, so I'm interested as well. I'll take a look with Ruben to push this PR again. |
Signed-off-by: Uilian Ries <[email protected]>
conan/tools/env/environment.py
Outdated
value = varvalues.get_str("${name}", self._subsystem, pathsep=self._pathsep) | ||
value = value.replace('"', '\\"') | ||
if value: | ||
result.append('set -gx {} "{}"'.format(varname, value)) | ||
else: | ||
result.append('set -ge {}'.format(varname)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it could be simplified by using set -p
(prepend), instead of expanding all values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit risky in its current form, because it breaks when applied to dependencies or conan build
conan/tools/env/environment.py
Outdated
result.append('set -ge {}'.format(varname)) | ||
|
||
content = "\n".join(result) | ||
content = relativize_generated_file(content, self._conanfile, "$script_folder") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it possible to not use this and relativize paths as the other environment do?
else: # Need to deduce it automatically | ||
is_bat = self._subsystem == WINDOWS | ||
is_ps1 = self._conanfile.conf.get("tools.env.virtualenv:powershell", check_type=bool) | ||
if is_ps1: | ||
is_fish = self._conanfile.conf.get("tools.env.virtualenv:fish", check_type=bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are no wrappers for .fish
files, are they? If there are no wrappers, this will break when set and used to build any dependency.
This should most likely apply only to end-consumer conanfile, not cache dependencies, and only if not used for conan build
or anything like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, please, take a look in the test test_transitive_tool_requires
. It generates a tool requirement, then a second recipe consumes it and runs an executable that prints an environment variable. All using Fish.
conan/tools/env/environment.py
Outdated
deactivate = ("""echo "echo Nothing to restore" > {deactivate_file}""" | ||
.format(deactivate_file=deactivate_file)) | ||
else: | ||
deactivate = textwrap.dedent("""\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For new one, I'd try to do directly an in-memory or in-tmp-folder deactivate script rather than a local generated one.
To understand the current error when trying to use
So, it's incompatible with Fish right now. Just to have an idea, I'll check if we can do something compatible with both, because it's a really small script that we have. |
Add new test to validate consuming a tool requirement using Fish shell. It should be able to load the tool without errors. |
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
conan/tools/env/environment.py
Outdated
# TODO: This implemantation is dirty, improve it | ||
for f in filenames: | ||
f = f if os.path.isabs(f) else os.path.join(env_folder, f) | ||
if f.lower().endswith(".sh"): | ||
if os.path.isfile(f) and "sh" in accept: | ||
f = subsystem_path(subsystem, f) | ||
shs.append(f) | ||
elif f.lower().endswith(".fish"): | ||
if os.path.isfile(f) and "fish" in accept: | ||
f = subsystem_path(subsystem, f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be supporting fish at the moment in Windows subsystems, mostly used externally with win_bash
conan/tools/env/environment.py
Outdated
@@ -62,6 +73,10 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, | |||
elif shs: | |||
launchers = " && ".join('. "{}"'.format(f) for f in shs) | |||
return '{} && {}'.format(launchers, cmd) | |||
elif fishs: | |||
launchers = " && ".join('. "{}"'.format(f) for f in fishs) | |||
print(f"LAUNCHERS: {launchers}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
print(f"LAUNCHERS: {launchers}") |
conan/tools/env/environment.py
Outdated
function {{ function_name }} | ||
echo "echo Restoring environment" | ||
{% for item in vars_unset %} | ||
set -e {{ item }} | ||
{% endfor %} | ||
{% for item, value in vars_restore.items() %} | ||
set -gx {{ item }} {{ value }} | ||
{% endfor %} | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The restore should work restoring the variables that were defined at activation time, not the variables that were defined at the time of generating the script. It might be a bit more complicated, the activation needs to store the variables in memory or something too, so the restore can use them
conans/client/generators/__init__.py
Outdated
@@ -167,6 +168,8 @@ def deactivates(filenames): | |||
bats.append("%~dp0/"+path) | |||
elif env_script.endswith(".sh"): | |||
shs.append(subsystem_path(subsystem, path)) | |||
elif env_script.endswith(".fish"): | |||
fishs.append(subsystem_path(subsystem, path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, no subsystem for fish initially, IMHO
@memsharded Thank you for your early review. I just talked to @RubenRBS few minutes ago and found other cases to be testes using spaces and some alternatives for deactivation via memory function. Most of these changes were made to track the creation of |
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! I think it is almost ready.
:param file_location: The path to the file to save the fish script. | ||
""" | ||
filepath, filename = os.path.split(file_location) | ||
function_name = f"deactivate_{Path(filename).stem}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the function_name is this one? Why not just the group
with the random chars? Is there risk of collision of deactivate_xxxx
function name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It follow the common name used by other virtualenv like: conanbuild.sh
-> deactivate_conanbuild.sh
. So you can actually can call from your terminal directly, like:
source conanbuild.fish
...
deactivate_conanbuild
In Fish, functions are exported as a command, so you can run directly.
About the random to avoid collision: In case we have multiple dependencies add bin folder to PATH, we will need to undo the configuration after deactivating it. Usually it would be store in a deactivate script, but here we are using a variable. Actually, the random is just an idea, but could be the build folder name too, because is kind unique name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, having something deterministic would make more sense, I probably still didn't understand the issue.
conan/tools/env/environment.py
Outdated
{% if item == "PATH" %} | ||
set -g conan_{{ group }}_{{ item }} "${{ item }}" | ||
fish_add_path -pg "{{ value }}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are other variables that are paths too, like PYTHONPATH, and every other var marked as "path", shouldn't they do the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately fish_add_path
is a feature only to PATH: https://fishshell.com/docs/current/cmds/fish_add_path.html
I didn't find a similar feature for custom paths, but I'll check with Rubén tomorrow for insights.
I'm not sure about the logic that I added: In case a variable already exist, it will be prepend to that variable. Pros: Preserves system variables and variables already configured by another Conan dependency. Cons: If an user runs I still need to fix some important points when manipulating paths, it's not working right now when needing to prepend. |
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
This comment was marked as resolved.
This comment was marked as resolved.
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Fish is now installed and configured with tests. Time for review! |
from conans.util.files import save | ||
|
||
# INFO: Fish only supports Cygwin and WSL https://github.com/fish-shell/fish-shell?tab=readme-ov-file#windows | ||
pytestmark = pytest.mark.skipif(platform.system() not in ("Darwin", "Linux"), reason="Fish is well supported only in Linux and MacOS") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this syntax and not a normal @pytest.mark.skip?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the entire file only work with Linux and MacOS. Otherwise, would need to apply for each test. It's a documented feature in pytest: https://docs.pytest.org/en/latest/example/markers.html#marking-whole-classes-or-modules
return ". " + " && . ".join('"{}"'.format(s) for s in files) | ||
fishs = [it for it in fishs if os.path.exists(it)] | ||
if fishs: | ||
filename = "conan{}.fish".format(group) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it seems it is missing the deactivation function (not script)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the deactivation is not an script, but a function exported by Fish in the environment. So we can call the deactivate_conanbuild, but there is no file associated. The function is created on-the-fly: https://github.com/conan-io/conan/pull/15503/files#diff-56b36c23589a66b0bd803c545703684c74400cbb7bd8538b8949ed84613e3fc7R550
:param file_location: The path to the file to save the fish script. | ||
""" | ||
filepath, filename = os.path.split(file_location) | ||
function_name = f"deactivate_{Path(filename).stem}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, having something deterministic would make more sense, I probably still didn't understand the issue.
Signed-off-by: Uilian Ries <[email protected]>
Signed-off-by: Uilian Ries <[email protected]>
Changelog: Feature: Add Fish shell command support for environments
Docs: conan-io/docs#3690
Still need to check if the
-g
scope for the variable erasure is ok, and how activating both:powershell
and:fish
confs work